package org.toools.twitter;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Vector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;

/**
 * checks your twitter account for in- and outbound twitter @replies (status updates containing "@<username>")
 * and automatically follows every twitter user that you or the users you follow send messages to or that you get @replies from.
 *
 * @author stefan asseg
 */
public class Main {

    private static final Log log = LogFactory.getLog(Main.class);
    private static final String configFile = "toools-twitter_config.properties";
    private static final String saveFile = "toools-twitter_save";
    private static Properties properties = new Properties();
    private static String twitter_user;
    private static String twitter_pass;
    private static Twitter twitter;
    private static int twitterRequests = 0;
    private static HashMap<String, Vector<String>> usersFollowing;

    public static void main(String args[]) {
        readConfig();
        restoreUsersFollowing();
        initApi();

        try {

            // check replies directed to own username
            log.info("checking replies directed to own username:");
            twitterRequests++;
            for (Status s : twitter.getReplies()) {
                String sender = s.getUser().getScreenName();
                log.debug("sender = [" + sender + "], reply = [" + s.getText() + "]");
                possiblyFollowUser(sender);
            }

            // check status updates of users following and own ones
            log.info("checking friendsTimeLine status updates:");
            twitterRequests++;
            for (Status s : twitter.getFriendsTimeline()) {
                String sender = s.getUser().getScreenName();
                log.debug("sender = [" + sender + "], status = [" + s.getText() + "] usernames: ");
                for (String username : extractUsernames(s.getText())) {
                    log.debug("|-- username = [" + username + "]");
                    possiblyFollowUser(username);
                }
            }

        } catch (TwitterException e) {
            log.error("error when communicating with twitter.", e);
        }

        log.info("total twitter requests performed: " + twitterRequests);
        try {
            log.info("remaining twitter requests for the current hour: " + twitter.rateLimitStatus().getRemainingHits());
        } catch (TwitterException e) {
            log.error("error when communicating with twitter.", e);
        }

        persistUsersFollowing();
    }

    private static void readConfig() {
        try {
            properties.load(new FileInputStream(configFile));
            twitter_user = properties.getProperty("twitter_user");
            twitter_pass = properties.getProperty("twitter_pass");
            properties.clear();
        } catch (IOException e) {
            log.error("error when reading config file", e);
        }
    }

    private static void restoreUsersFollowing() {
        File usersFollowingFile = new File(saveFile);
        if (usersFollowingFile.exists()) {
            try {
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream(usersFollowingFile));
                usersFollowing = (HashMap<String, Vector<String>>) ois.readObject();
                ois.close();
            } catch (Exception e) {
                System.err.println("Could not restore users following from file \"" + saveFile + "\".");
            }
        } else {
            usersFollowing = new HashMap<String, Vector<String>>();
        }

        if (!usersFollowing.containsKey(twitter_user)) {
            usersFollowing.put(twitter_user, new Vector<String>());
        }
    }

    private static void persistUsersFollowing() {
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(saveFile));
            oos.writeObject(usersFollowing);
            oos.close();
        } catch (IOException e) {
            System.err.println("Could not persist users following to file \"" + saveFile + "\".");
        }
    }

    private static void initApi() {
        twitter = new Twitter(twitter_user, twitter_pass);
    }

    private static String[] extractUsernames(String string) {
        Vector<String> usernames = new Vector<String>();
        for (String piece : string.split(" ")) {
            if (piece.startsWith("@")) {
                String possibleUsername = piece.substring(1);

                // cut off all characters from the end of the possibleUsername that ore not either alphabetical, numeric or _
                while (possibleUsername.length() > 0 && !possibleUsername.matches("(\\w)*")) {
                    possibleUsername = possibleUsername.substring(0, possibleUsername.length() - 1);
                }
                if (possibleUsername.length() > 0) {
                    usernames.add(possibleUsername);
                }
            }
        }
        return usernames.toArray(new String[usernames.size()]);
    }

    private static void possiblyFollowUser(String username) {
        if (!getTwitterUser().equals(username)) {
            boolean alreadyFollowingAndPersisted = usersFollowing.get(twitter_user).contains(username);
            int followingStatus = alreadyFollowingAndPersisted ? 1 : 0;
            if (followingStatus == 0) {
                try {
                    twitterRequests++;
                    log.debug("checking followingStatus for " + username + ".");
                    followingStatus = twitter.exists(twitter_user, username) ? 1 : 2;
                } catch (TwitterException e) {
                    log.info(username + ": no existing twitter username or user's updates are protected and permission to follow already requested.");
                }
            }

            if (followingStatus == 2) {
                try {
                    twitterRequests++;
                    twitter.createAndFollow(username);
                    log.info("now following " + username);
                } catch (TwitterException e) {
                    log.error("could not follow \"" + username + "\"", e);
                }
            } else if (followingStatus == 1) {
                log.debug("already following " + username);
            }

            if (!alreadyFollowingAndPersisted && followingStatus != 0) {
                usersFollowing.get(twitter_user).add(username);
                log.debug("adding " + username + " to the persistent list of users we follow");
            }
        }
    }

    private static String getTwitterUser() {
        return twitter_user;
    }
}